﻿using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace Boxman {
    // 游戏
    class Game {
        // 回调
        public interface Callback {
            void OnGameOver(bool allOver);  // 游戏结束事件
            void OnGotoLevel(int level);    // 进入关卡事件
            void OnManMoved(int moveCount); // 小人移动事件
        }
        
        // 移动结点
        private class MoveNode {
            public BaseGameObject Box { get; }  // 被推动的箱子
            public Keys Direction { get; }      // 移动方向

            /**
             * 构造方法。
             * @param box       被推动的箱子
             * @param direction 移动方向
             */
            public MoveNode(BaseGameObject box, Keys direction) {
                Box = box;
                Direction = direction;
            }
        }

        // 状态
        public enum Status {
            END,    // 已结束
            PLAYING // 游戏中
        }

        public int Level { get; private set; }              // 关卡数
        private int mBoxCount;                              // 未到达目的地的箱子数
        public readonly Brush mBrush;                       // 背景画刷
        private readonly Callback mCallback;                // 回调
        private readonly Graphics mDrawer;                  // 绘图者
        private readonly Control mGameBoard;                // 游戏区域
        private Man mMan;                                   // 小人
        private int mMoveCount;                             // 移动次数
        private readonly LinkedList<MoveNode> mMoveList;    // 所有移动结点
        private Status mStatus;                             // 状态
        public static BaseGameObject[,] sBaseGameObjects;   // 所有游戏物体

        /**
         * 构造方法。
         * @param callback  回调
         * @param drawer    绘图者
         * @param gameBoard 游戏区域
         */
        public Game(Callback callback, Graphics drawer, Control gameBoard) {
            // 初始化成员
            Level = 1;
            mBoxCount = 0;
            mCallback = callback;
            mDrawer = drawer;
            mGameBoard = gameBoard;
            mBrush = new SolidBrush(Const.GAME_BOARD_COLOR);
            mMoveCount = 0;
            mMoveList = new LinkedList<MoveNode>();
            mStatus = Status.END;

            // 进入第1关
            this.EnterLevel();
        }

        /**
         * 进入关卡。
         */
        public void EnterLevel() {
            // 小人停止跳舞
            if (mMan != null) {
                mMan.StopDancing();
            }

            // 重绘游戏区域
            mDrawer.FillRectangle(mBrush, 0, 0, Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT);

            // 删除所有物体
            sBaseGameObjects = new BaseGameObject[Const.GAME_BOARD_GRID_COUNT_V, Const.GAME_BOARD_GRID_COUNT_H];

            // 删除所有移动结点
            mMoveList.Clear();
            
            // 读取关卡文件
            string levelFilePath = string.Format(Const.LEVEL_FILE_PATH, Level);
            StreamReader reader = File.OpenText(levelFilePath);

            // 创建所有物体
            int read = -1;
            BaseGameObject.Type type;
            mBoxCount = 0;
            for (int y = 0; y < Const.GAME_BOARD_GRID_COUNT_V; y++) {
                for (int x = 0; x < Const.GAME_BOARD_GRID_COUNT_H; x++) {
                    read = reader.Read();
                    if (read == '0') {
                        continue;
                    }
                    if (read == '\r' || read == '\n') {
                        x--;
                        continue;
                    }

                    type = (BaseGameObject.Type)(read - '0');

                    // 创建箱子
                    if (type == BaseGameObject.Type.BOX) {
                        sBaseGameObjects[y, x] = new Box(new BaseGameObject(mDrawer, BaseGameObject.Type.BACKGROUND, x, y), mDrawer, type, x, y);
                        mBoxCount++;
                        continue;
                    }
 
                    // 创建小人
                    if (type == BaseGameObject.Type.MAN) {
                        sBaseGameObjects[y, x] = mMan = new Man(new BaseGameObject(mDrawer, BaseGameObject.Type.BACKGROUND, x, y), mDrawer, mGameBoard, type, x, y);
                        continue;
                    }

                    // 创建背景、目的地、墙
                    sBaseGameObjects[y, x] = new BaseGameObject(mDrawer, type, x, y);
                }
            }

            // 关闭文件
            reader.Close();

            // 重置成员变量
            mMoveCount = 0;
            mStatus = Status.PLAYING;

            // 刷新游戏区域
            mGameBoard.Invalidate(false);

            // 发送通知
            mCallback.OnGotoLevel(Level);
        }

        /**
         * 游戏结束。
         * @param allOver   全部通关则为 true，否则为 false
         */
        private void GameOver(bool allOver) {
            // 将状态设置为已结束
            mStatus = Status.END;

            // 发送通知
            mCallback.OnGameOver(allOver);
        }

        /**
         * 进入指定的关卡。
         * @param level 关卡数
         */
        public void GotoLevel(int level) {
            if (level <= Const.LEVEL_COUNT) {
                Level = level;
                this.EnterLevel();
            }
        }

        /**
         * 进入下一关。
         */
        private void GotoNextLevel() {
            if (Level < Const.LEVEL_COUNT) {
                Level++;
                this.EnterLevel();
            }
        }

        /**
         * 判断是否胜利。
         * @return 胜利则返回 true，否则返回 false
         */
        private bool IsWon() {
            return mBoxCount == 0;
        }

        /**
         * 按键事件的响应方法。
         * @param key   按键
         * @return 事件在此被处理则返回 true，否则返回 false
         */
        public bool OnKeyPressed(Keys key) {
            // 如果没在玩，则直接返回
            if (mStatus != Status.PLAYING) {
                return false;
            }

            // 如果按的不是方向键，则直接返回
            if (key < Keys.Left || key > Keys.Down) {
                return false;
            }

            // 移动小人。如果不能移动，则直接返回
            BaseGameObject.MoveResult moveResult = BaseGameObject.MoveResult.CANNOT_MOVE;
            BaseGameObject movedBox = mMan.Move(key, ref moveResult);
            if (moveResult <= BaseGameObject.MoveResult.NO_MOVE) {
                return true;
            }

            // 重绘游戏区域
            mGameBoard.Invalidate(false);

            // 增加移动次数
            mMoveCount++;

            // 发送通知
            mCallback.OnManMoved(mMoveCount);

            // 判断是否胜利
            if (moveResult == BaseGameObject.MoveResult.ARRIVED_ON_DEST) {
                mBoxCount--;
                if (this.IsWon()) {
                    bool allOver = Level == Const.LEVEL_COUNT;
                    this.GameOver(allOver);
                    if (!allOver) {
                        this.GotoNextLevel();
                    }
                    return true;
                }
            } else if (moveResult == BaseGameObject.MoveResult.LEFT_FROM_DEST) {
                mBoxCount++;
            }

            // 将本次移动加入列表
            mMoveList.AddLast(new MoveNode(movedBox, key));
            return true;
        }

        /**
         * 重玩本关。
         */
        public void Restart() {
            this.EnterLevel();
        }

        /**
         * 回退，即撤消上一次操作。
         */
        public void Undo() {
            if (mMoveCount == 0) {
                return;
            }

            // 计算方向（方向与最后一次移动的方向相反）
            Keys direction = 0;
            MoveNode moveListNode = mMoveList.Last.Value;
            switch (moveListNode.Direction) {
                case Keys.Down:
                    direction = Keys.Up;
                    break;
                case Keys.Left:
                    direction = Keys.Right;
                    break;
                case Keys.Right:
                    direction = Keys.Left;
                    break;
                case Keys.Up:
                    direction = Keys.Down;
                    break;
            }

            // 移动小人
            BaseGameObject.MoveResult moveResult = BaseGameObject.MoveResult.CANNOT_MOVE;
            mMan.Move(direction, ref moveResult);

            // 移动箱子
            if (moveListNode.Box != null) {
                moveListNode.Box.Move(direction, ref moveResult);
                if (moveResult == BaseGameObject.MoveResult.LEFT_FROM_DEST) {
                    mBoxCount++;
                } else if (moveResult == BaseGameObject.MoveResult.ARRIVED_ON_DEST) {
                    mBoxCount--;
                }
            }

            // 删除当前的移动结点
            mMoveList.RemoveLast();

            // 发送通知
            mMoveCount--;
            mCallback.OnManMoved(mMoveCount);
        }
    }
}
